/*
  +----------------------------------------------------------------------+
  | PHP Version 5                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2013 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3.01 of the PHP license,      |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://www.php.net/license/3_01.txt                                  |
  | If you did not receive a copy of the PHP license and are unable to   |
  | obtain it through the world-wide-web, please send a note to          |
  | license@php.net so we can mail you a copy immediately.               |
  +----------------------------------------------------------------------+
  | Author:                                                              |
  +----------------------------------------------------------------------+
*/

/* $Id$ */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "main/SAPI.h"  
#include "Zend/zend_interfaces.h"  
#include "ext/standard/php_var.h"  
#include "ext/standard/php_string.h"  
#include "ext/standard/php_smart_str.h"  
#include "ext/standard/php_filestat.h"
#include "ext/standard/url.h"
#include "ext/standard/php_string.h"
#include "ext/standard/php_array.h"
#include "php_wk_framework.h"
#include "wk_view.h"
#include "wk_load.h"
#include "wk_controller.h"
#include "wk_model.h"
#include "wk_config.h"
#include "wk_input.h"
 
/* If you declare any globals in php_wk_framework.h uncomment this:
ZEND_DECLARE_MODULE_GLOBALS(wk_framework)
*/

zend_class_entry *wk_framework_ce;
static int le_wk_framework;

/* {{{ wk_framework_functions[]
 *
 * Every user visible function must have an entry in wk_framework_functions[].
 */
const zend_function_entry wk_framework_functions[] = {
    PHP_FE(wk_get_instance,	NULL)		/* For testing, remove later. */
	{NULL, NULL, NULL}	/* Must be the last line in wk_framework_functions[] */
};
/* }}} */

/* {{{ wk_framework_module_entry
 */
zend_module_entry wk_framework_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
	STANDARD_MODULE_HEADER,
#endif
	"wk_framework",
	wk_framework_functions,
	PHP_MINIT(wk_framework),
	PHP_MSHUTDOWN(wk_framework),
	PHP_RINIT(wk_framework),		/* Replace with NULL if there's nothing to do at request start */
	PHP_RSHUTDOWN(wk_framework),	/* Replace with NULL if there's nothing to do at request end */
	PHP_MINFO(wk_framework),
#if ZEND_MODULE_API_NO >= 20010901
	PHP_WK_FRAMEWORK_VERSION,
#endif
	STANDARD_MODULE_PROPERTIES
};
/* }}} */

#ifdef COMPILE_DL_WK_FRAMEWORK
ZEND_GET_MODULE(wk_framework)
#endif

/* {{{ PHP_INI
 */
/* Remove comments and fill if you need to have entries in php.ini
PHP_INI_BEGIN()
	STD_PHP_INI_ENTRY("wk_framework.global_value",      "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_wk_framework_globals, wk_framework_globals)
	STD_PHP_INI_ENTRY("wk_framework.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_wk_framework_globals, wk_framework_globals)
PHP_INI_END()
*/
/* }}} */

/* {{{ php_wk_framework_init_globals
 */
/* Uncomment this function if you have INI entries
static void php_wk_framework_init_globals(zend_wk_framework_globals *wk_framework_globals)
{
	wk_framework_globals->global_value = 0;
	wk_framework_globals->global_string = NULL;
}
*/
/* }}} */

ZEND_BEGIN_ARG_INFO_EX(void_arginfo, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(setCompileDir_arginfo, 0, 0, 1)  
ZEND_ARG_INFO(0, path)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(setTemplateDir_arginfo, 0, 0, 1)  
ZEND_ARG_INFO(0, path)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(loadData_arginfo, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(captureRouter_arginfo, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(setApplicationPath_arginfo, 0, 0, 1)
ZEND_ARG_INFO(0, apppath)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(setEnvironment_arginfo, 0, 0, 1)
ZEND_ARG_INFO(0, environment)
ZEND_END_ARG_INFO()


zend_class_entry *get_class_entry(char *class_name, int class_name_len TSRMLS_DC){
	zend_class_entry **ce;

	if (zend_lookup_class(class_name, class_name_len, &ce TSRMLS_CC) == SUCCESS) {
		return *ce;
	} else {
		return NULL;
	}
}

zval * request_var(uint type, char * name, uint len TSRMLS_DC) {
	zval 		**carrier = NULL, **ret;

	zend_bool 	jit_initialization = PG(auto_globals_jit);

	switch (type) {
		case POST:
		case GET:
		case FILES:
		case COOKIE:
			carrier = &PG(http_globals)[type];
			break;
		case ENV:
			if (jit_initialization) {
				zend_is_auto_global(ZEND_STRL("_ENV") TSRMLS_CC);
			}
			carrier = &PG(http_globals)[type];
			break;
		case SERVER:
			if (jit_initialization) {
				zend_is_auto_global(ZEND_STRL("_SERVER") TSRMLS_CC);
			}
			carrier = &PG(http_globals)[type];
			break;
		case REQUEST:
			if (jit_initialization) {
				zend_is_auto_global(ZEND_STRL("_REQUEST") TSRMLS_CC);
			}
			(void)zend_hash_find(&EG(symbol_table), ZEND_STRS("_REQUEST"), (void **)&carrier);
			break;
		default:
			break;
	}

	if (!carrier || !(*carrier)) {
		zval *empty;
		MAKE_STD_ZVAL(empty);
		ZVAL_NULL(empty);
		return empty;
	}

	if (!len) {
		Z_ADDREF_P(*carrier);
		return *carrier;
	}

	if (zend_hash_find(Z_ARRVAL_PP(carrier), name, len + 1, (void **)&ret) == FAILURE) {
		zval *empty;
		MAKE_STD_ZVAL(empty);
		ZVAL_STRINGL(empty, "", 0, 0);
		return empty;
	}

	Z_ADDREF_P(*ret);
	return *ret;
}

int active_symbol_include(char *path, include_callback cbk){
	zend_op_array *op_array;
	HashTable *calling_symbol_table;
	zend_file_handle file_handle;

	if(zend_stream_open(path, &file_handle TSRMLS_CC) == SUCCESS){
		zval **fooval;
		op_array = zend_compile_file(&file_handle, ZEND_REQUIRE TSRMLS_CC);

		if (EG(active_symbol_table)) {
			calling_symbol_table = EG(active_symbol_table);
		} else {
			calling_symbol_table = NULL;
		}

		ALLOC_HASHTABLE(EG(active_symbol_table));
		zend_hash_init(EG(active_symbol_table), 0, NULL, ZVAL_PTR_DTOR, 0);

		if (op_array && file_handle.handle.stream.handle) {
			int dummy = 1;
			zend_hash_add(&EG(included_files), file_handle.opened_path, strlen(file_handle.opened_path)+1, (void *)&dummy, sizeof(int), NULL);
		}
		zend_destroy_file_handle(&file_handle TSRMLS_CC);

		if (op_array) {
			zval *result					= NULL;
			STORE_EG_ENVIRON();

			EG(return_value_ptr_ptr)	= &result;
			EG(active_op_array) 		= op_array;

	#if ((PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION > 2)) || (PHP_MAJOR_VERSION > 5)
			if (!EG(active_symbol_table)) {
				zend_rebuild_symbol_table(TSRMLS_C);
			}
	#endif
			zend_execute(op_array TSRMLS_CC);

			destroy_op_array(op_array TSRMLS_CC);
			efree(op_array);

			if (!EG(exception)) {
				if (EG(return_value_ptr_ptr)) {
					zval_ptr_dtor(EG(return_value_ptr_ptr));
				}
			}

			RESTORE_EG_ENVIRON();
		}

		if(cbk != NULL){
			cbk(EG(active_symbol_table));
		}

		if (calling_symbol_table) {
			FREE_HASHTABLE(EG(active_symbol_table));
			EG(active_symbol_table) = calling_symbol_table;
		}
	}else{
		zend_error(E_WARNING, "%s does not open", path);
		return 1;
	}
}

int include(char *path){
	zend_op_array *op_array;
	zend_file_handle file_handle;

	if(zend_stream_open(path, &file_handle TSRMLS_CC) == SUCCESS){
		zval **fooval;
		op_array = zend_compile_file(&file_handle, ZEND_REQUIRE TSRMLS_CC);

		ALLOC_HASHTABLE(EG(active_symbol_table));
		zend_hash_init(EG(active_symbol_table), 0, NULL, ZVAL_PTR_DTOR, 0);

		if (op_array && file_handle.handle.stream.handle) {
			int dummy = 1;
			zend_hash_add(&EG(included_files), file_handle.opened_path, strlen(file_handle.opened_path)+1, (void *)&dummy, sizeof(int), NULL);
		}
		zend_destroy_file_handle(&file_handle TSRMLS_CC);

		if (op_array) {
			zval *result					= NULL;
			STORE_EG_ENVIRON();

			EG(return_value_ptr_ptr)	= &result;
			EG(active_op_array) 		= op_array;

	#if ((PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION > 2)) || (PHP_MAJOR_VERSION > 5)
			if (!EG(active_symbol_table)) {
				zend_rebuild_symbol_table(TSRMLS_C);
			}
	#endif
			zend_execute(op_array TSRMLS_CC);

			destroy_op_array(op_array TSRMLS_CC);
			efree(op_array);

			if (!EG(exception)) {
				if (EG(return_value_ptr_ptr)) {
					zval_ptr_dtor(EG(return_value_ptr_ptr));
				}
			}

			RESTORE_EG_ENVIRON();
		}

	}else{
		zend_error(E_ERROR, "%s does not open", path);
		return 1;
	}
}

char *get_config_path(char *filename, int len){
	zval *app_path, *environment, controller_exists;
	zval **config;
	smart_str filepath = {0};
	
	app_path = zend_read_static_property(wk_framework_ce, ZEND_STRL("apppath"), 0 TSRMLS_CC);
	environment = zend_read_static_property(wk_framework_ce, ZEND_STRL("environment"), 0 TSRMLS_CC);

	//加载config.php
	smart_str_appendl(&filepath, Z_STRVAL_P(app_path), Z_STRLEN_P(app_path));
	smart_str_appendl(&filepath, "config/", 7);
	convert_to_string(environment);
	if(Z_STRLEN_P(environment) > 0 && strcmp("development", Z_STRVAL_P(environment)) != 0){
		smart_str_appendl(&filepath, Z_STRVAL_P(environment), Z_STRLEN_P(environment));
		smart_str_appendl(&filepath, "/", 1);
	}
	smart_str_appendl(&filepath, filename, len);
	smart_str_0(&filepath);

	php_stat(filepath.c, filepath.len, FS_EXISTS, &controller_exists TSRMLS_CC);
	if(Z_LVAL(controller_exists) == 1){
		return estrdup(filepath.c);
	}else{
		smart_str filepath = {0};
		smart_str_appendl(&filepath, Z_STRVAL_P(app_path), Z_STRLEN_P(app_path));
		smart_str_appendl(&filepath, "config/", 7);
		smart_str_appendl(&filepath, filename, len);
		smart_str_0(&filepath);
		return estrdup(filepath.c);
	}
}

ZEND_METHOD(wk_framework, __construct){
	zval *instance;
	instance = zend_read_static_property(wk_framework_ce, ZEND_STRL("framework"), 0 TSRMLS_CC);
	
	if (IS_OBJECT == Z_TYPE_P(instance)
			&& instanceof_function(Z_OBJCE_P(instance), wk_framework_ce TSRMLS_CC)) {
		RETURN_ZVAL(instance, 1, 0);
	}

	if(ZVAL_IS_NULL(instance)){
		zend_update_static_property(wk_framework_ce, ZEND_STRL("framework"), getThis() TSRMLS_CC);
		RETURN_ZVAL(instance, 1, 0);
	}else if(Z_TYPE_P(instance) == IS_OBJECT){
		RETURN_ZVAL(instance, 1, 0);
	}else{
		zend_error(E_ERROR, "wk_framework::__construct failed!");
		RETURN_FALSE;
	}
}

ZEND_METHOD(wk_framework, loadCore){
	
}

ZEND_METHOD(wk_framework, loadView){
	zval *view_obj;
	view_obj = zend_read_static_property(wk_framework_ce, ZEND_STRL("view"), 1 TSRMLS_CC);

	if( Z_TYPE_P(view_obj) == NULL){
		MAKE_STD_ZVAL(view_obj);
		object_init_ex(view_obj, ext_view_ce);
		zend_update_static_property(wk_framework_ce, ZEND_STRL("view"), view_obj TSRMLS_CC);
	}

	zval_copy_ctor(view_obj);

	RETURN_TRUE;
}

ZEND_METHOD(wk_framework, setCompileDir){
	zval *path, *view_obj;

	if( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &path) == FAILURE){
		RETURN_FALSE;
	}

	view_obj = zend_read_static_property(wk_framework_ce, ZEND_STRL("view"), 0 TSRMLS_CC);

	if( Z_TYPE_P(view_obj) == NULL){
		zend_error(E_ERROR, "please first run wk_framework::loadView");
		RETURN_FALSE;
	}

	convert_to_string(path);

	zend_call_method_with_1_params(&view_obj, NULL, NULL, "setCompileDir", &return_value, path);
}

ZEND_METHOD(wk_framework, setTemplateDir){
	zval *path, *view_obj;
	zend_class_entry *view_ce;

	if( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &path) == FAILURE){
		RETURN_FALSE;
	}

	view_obj = zend_read_static_property(wk_framework_ce, ZEND_STRL("view"), 0 TSRMLS_CC);

	if( Z_TYPE_P(view_obj) == NULL){
		zend_error(E_ERROR, "please first run wk_framework::loadView");
		RETURN_FALSE;
	}

	convert_to_string(path);

	zend_call_method_with_1_params(&view_obj, NULL, NULL, "setTemplateDir", &return_value, path);
}

ZEND_METHOD(wk_framework, setApplicationPath){
	zval *apppath, *base_path;
	char *ret;
	int basepath_len;

	if( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &apppath) == FAILURE){
		RETURN_FALSE;
	}

	if(Z_TYPE_P(apppath) != IS_STRING){
		convert_to_string(apppath);
	}
	
	zend_update_static_property(wk_framework_ce, ZEND_STRL("apppath"), apppath TSRMLS_CC);

	ret = estrndup(Z_STRVAL_P(apppath), Z_STRLEN_P(apppath));
	basepath_len = php_dirname(ret, Z_STRLEN_P(apppath)) + 1;
	MAKE_STD_ZVAL(base_path);
	ZVAL_STRINGL(base_path, Z_STRVAL_P(apppath), basepath_len, 0);
	zend_update_static_property(wk_framework_ce, ZEND_STRL("basepath"), base_path TSRMLS_CC);
	efree(ret);
}

ZEND_METHOD(wk_framework, setEnvironment){
	zval *environment;

	if( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &environment) == FAILURE){
		RETURN_FALSE;
	}

	if(Z_TYPE_P(environment) != IS_STRING){
		convert_to_string(environment);
	}
	
	zend_update_static_property(wk_framework_ce, ZEND_STRL("environment"), environment TSRMLS_CC);
}

ZEND_METHOD(wk_framework, captureRouter){
	zval **config;
	zval **cfg_index_page, 
		**cfg_uri_protocol, 
		**cfg_url_suffix, 
		**cfg_language, 
		**cfg_charset, 
		**cfg_subclass_prefix,
		**cfg_controller_trigger,
		**cfg_function_trigger,
		**cfg_directory_trigger;
	zval **default_controller, **override;
	zval **route;
	zval *controller, *method, *directory;
	zval app_path, controller_exists;
	
	char *configpath = get_config_path("config.php", 10);
	char *routepath = get_config_path("routes.php", 10);
	char *uri_protocol;

	include(configpath);
	efree(configpath);

	if( zend_hash_find( EG(active_symbol_table), "config", sizeof("config"), (void**)&config) != SUCCESS){
		MAKE_STD_ZVAL(*config);
		ZVAL_NULL(*config);
	}

	include(routepath);
	efree(routepath);
	
	if( zend_hash_find( EG(active_symbol_table), "route", sizeof("route"), (void**)&route) != SUCCESS){
		MAKE_STD_ZVAL(*route);
		ZVAL_NULL(*route);
	}
	
	get_array_item_p(Z_ARRVAL_PP(route), "default_controller", default_controller, "welcome", 7);
	get_array_item_p(Z_ARRVAL_PP(route), "404_override", override, "", 0);
	get_array_item_p(Z_ARRVAL_PP(config), "index_page", cfg_index_page, "index", 9);
	get_array_item_p(Z_ARRVAL_PP(config), "uri_protocol", cfg_uri_protocol, "query_string", 12);
	get_array_item_p(Z_ARRVAL_PP(config), "url_suffix", cfg_url_suffix, "", 0);
	get_array_item_p(Z_ARRVAL_PP(config), "language", cfg_language, "zh-cn", 5);
	get_array_item_p(Z_ARRVAL_PP(config), "charset", cfg_charset, "utf-8", 5);
	get_array_item_p(Z_ARRVAL_PP(config), "subclass_prefix", cfg_subclass_prefix, "WK_", 3);
	get_array_item_p(Z_ARRVAL_PP(config), "controller_trigger", cfg_controller_trigger, "c", 1);
	get_array_item_p(Z_ARRVAL_PP(config), "function_trigger", cfg_function_trigger, "a", 1);
	get_array_item_p(Z_ARRVAL_PP(config), "directory_trigger", cfg_directory_trigger, "d", 1);
	
	if(strcmp(sapi_module.name, "cli") == 0){
		zval *argv;
		argv = request_var(SERVER, ZEND_STRL("argv"));
		if(zend_hash_num_elements(Z_ARRVAL_P(argv)) >= 2){
			zval **cmds, delim, params_meta, *params, d;
			zend_bool jit_initialization = PG(auto_globals_jit);

			MAKE_STD_ZVAL(params);
			ZVAL_STRINGL(&delim, "&", 1, 1);
			array_init(&params_meta);
			array_init(params);
			ZVAL_STRINGL(&d, "=", 1, 1);

			if( zend_hash_index_find(Z_ARRVAL_P(argv), 1, (void**)&cmds) == SUCCESS){
				zval **operand, **controller_p, **method_p, **directory_p;
				php_explode(&delim, *cmds, &params_meta, 30);

				for (zend_hash_internal_pointer_reset(Z_ARRVAL(params_meta));
					zend_hash_get_current_data(Z_ARRVAL(params_meta), (void **)&operand) == SUCCESS;
					zend_hash_move_forward(Z_ARRVAL(params_meta))
				){
					zval param, **k, **v;

					array_init(&param);

					php_explode(&d, *operand, &param, 2);

					if( zend_hash_index_find(Z_ARRVAL(param), 0, (void**)&k) != SUCCESS ){
						MAKE_STD_ZVAL(*k);
						ZVAL_STRINGL(*k, "", 0, 1);
					}

					if( zend_hash_index_find(Z_ARRVAL(param), 1, (void**)&v) != SUCCESS ){
						MAKE_STD_ZVAL(*v);
						ZVAL_STRINGL(*v, "", 0, 1);
					}

					add_assoc_stringl(params, Z_STRVAL_PP(k), Z_STRVAL_PP(v), Z_STRLEN_PP(v), 1);
				}

				if( zend_hash_find(Z_ARRVAL_P(params), ZEND_STRS("c"), (void**)&controller_p) == FAILURE ){
					MAKE_STD_ZVAL(controller);
					ZVAL_STRINGL(controller, "welcome", 7, 1);
				}else{
					controller = *controller_p;
				}

				if( zend_hash_find(Z_ARRVAL_P(params), ZEND_STRS("m"), (void**)&method_p) == FAILURE ){
					MAKE_STD_ZVAL(method);
					ZVAL_STRINGL(method, "index", 5, 1);
				}else{
					method = *method_p;
				}

				if( zend_hash_find(Z_ARRVAL_P(params), ZEND_STRS("d"), (void**)&directory_p) == FAILURE ){
					MAKE_STD_ZVAL(directory);
					ZVAL_STRINGL(directory, "", 0, 1);
				}else{
					directory = *directory_p;
				}
				
				php_array_merge(Z_ARRVAL_P(PG(http_globals)[GET]), Z_ARRVAL_P(params), 0);
			}

			if( zend_hash_num_elements(Z_ARRVAL_P(argv)) == 4 ){
				zval **param, post_params;
				array_init(&post_params);
				if( zend_hash_index_find(Z_ARRVAL_P(argv), 2, (void**)&param) == SUCCESS ){
					zval **post_meta;

					if( strcmp( Z_STRVAL_PP(param), "-d" ) == 0 && 
						zend_hash_index_find(Z_ARRVAL_P(argv), 3, (void**)&post_meta) == SUCCESS
					){
						zval **operand, post;
						array_init(&post);
						php_explode(&delim, *post_meta, &post, 30);

						for (zend_hash_internal_pointer_reset(Z_ARRVAL(post));
							zend_hash_get_current_data(Z_ARRVAL(post), (void **)&operand) == SUCCESS;
							zend_hash_move_forward(Z_ARRVAL(post))
						){
							zval param, **k, **v;

							array_init(&param);

							php_explode(&d, *operand, &param, 2);

							if( zend_hash_index_find(Z_ARRVAL(param), 0, (void**)&k) != SUCCESS ){
								MAKE_STD_ZVAL(*k);
								ZVAL_STRINGL(*k, "", 0, 1);
							}

							if( zend_hash_index_find(Z_ARRVAL(param), 1, (void**)&v) != SUCCESS ){
								MAKE_STD_ZVAL(*v);
								ZVAL_STRINGL(*v, "", 0, 1);
							}

							if(Z_STRLEN_PP(k) == 0){
								continue;
							}

							add_assoc_stringl(&post_params, Z_STRVAL_PP(k), Z_STRVAL_PP(v), Z_STRLEN_PP(v), 1);
						}

						php_array_merge(Z_ARRVAL_P(PG(http_globals)[POST]), Z_ARRVAL(post_params), 0);
					}
				}
			}
		}else{
			MAKE_STD_ZVAL(controller);
			MAKE_STD_ZVAL(method);
			MAKE_STD_ZVAL(directory);
			ZVAL_STRINGL(controller, "welcome", 7, 1);
			ZVAL_STRINGL(method, "index", 5, 1);
			ZVAL_STRINGL(directory, "", 0, 1);
		}
	}else{
		uri_protocol = estrndup(Z_STRVAL_PP(cfg_uri_protocol), Z_STRLEN_PP(cfg_uri_protocol));
		uri_protocol = php_strtolower(uri_protocol, Z_STRLEN_PP(cfg_uri_protocol));

		if( strcmp(uri_protocol, "path_info") == 0 ){
			zval *request_uri, *delim, *path_arr;
			zval **operand, *new_path_arr;
			ulong path_cnt;
			HashPosition pos;
			char *string_key;
			uint string_key_len;

			request_uri = request_var(SERVER, ZEND_STRL("DOCUMENT_URI"));
			Z_STRVAL_P(request_uri) = php_str_to_str_ex(Z_STRVAL_P(request_uri), Z_STRLEN_P(request_uri),
													Z_STRVAL_PP(cfg_url_suffix), Z_STRLEN_PP(cfg_url_suffix),
													"", 0, &Z_STRLEN_P(request_uri), 1, NULL);

			MAKE_STD_ZVAL(delim);
			ZVAL_STRINGL(delim, "/", 1, 0);
			MAKE_STD_ZVAL(path_arr);
			array_init(path_arr);
			php_explode(delim, request_uri, path_arr, 20);

			MAKE_STD_ZVAL(new_path_arr);
			array_init(new_path_arr);

			for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(path_arr), &pos);
				zend_hash_get_current_data_ex(Z_ARRVAL_P(path_arr), (void **)&operand, &pos) == SUCCESS;
				zend_hash_move_forward_ex(Z_ARRVAL_P(path_arr), &pos)
			) {
				if( Z_STRLEN_PP(operand) > 0 && strcmp(Z_STRVAL_PP(operand), Z_STRVAL_PP(cfg_index_page)) != 0 ){
					add_next_index_stringl(new_path_arr, Z_STRVAL_PP(operand), Z_STRLEN_PP(operand), 1);
				}
			}
			if(path_cnt = zend_hash_num_elements(Z_ARRVAL_P(new_path_arr))){
				zval **controller_p, **method_p, **directory_p;

				if( path_cnt == 1 ){
					zend_hash_index_find(Z_ARRVAL_P(new_path_arr), path_cnt-1, (void**)&controller_p);
					MAKE_STD_ZVAL(method);
					MAKE_STD_ZVAL(directory);
					ZVAL_STRINGL(method, "index", 5, 1);
					ZVAL_STRINGL(directory, "", 0, 1);

					controller = *controller_p;
				}else {
					zval *directory_arr;
					MAKE_STD_ZVAL(directory);
					MAKE_STD_ZVAL(directory_arr);
					array_init(directory_arr);
					zend_hash_index_find(Z_ARRVAL_P(new_path_arr), path_cnt-2, (void**)&controller_p);
					zend_hash_index_find(Z_ARRVAL_P(new_path_arr), path_cnt-1, (void**)&method_p);

					for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(new_path_arr));
						zend_hash_has_more_elements(Z_ARRVAL_P(new_path_arr)) == SUCCESS;
						zend_hash_move_forward(Z_ARRVAL_P(new_path_arr))
					) {
						char *key;
						uint key_len;
						ulong idx;
						zval **data;

						zend_hash_get_current_key_ex(Z_ARRVAL_P(new_path_arr), &key, &key_len, &idx, 0, NULL);
						zend_hash_get_current_data(Z_ARRVAL_P(new_path_arr), (void**)&data);
						if(idx < path_cnt - 2){
							add_index_zval(directory_arr, idx, *data);
						}else{
							break;
						}
					}

					php_implode(delim, directory_arr, directory);
					controller = *controller_p;
					method = *method_p;
				}
			}else{
				MAKE_STD_ZVAL(controller);
				MAKE_STD_ZVAL(method);
				MAKE_STD_ZVAL(directory);
				ZVAL_STRINGL(controller, Z_STRVAL_PP(default_controller), Z_STRLEN_PP(default_controller), 1);
				ZVAL_STRINGL(method, "index", 5, 1);
				ZVAL_STRINGL(directory, "", 0, 1);
			}
		}else if( strcmp(uri_protocol, "query_string") == 0 ){
			controller = request_var(GET, Z_STRVAL_PP(cfg_controller_trigger), Z_STRLEN_PP(cfg_controller_trigger));
			method     = request_var(GET, Z_STRVAL_PP(cfg_function_trigger), Z_STRLEN_PP(cfg_function_trigger));
			directory  = request_var(GET, Z_STRVAL_PP(cfg_directory_trigger), Z_STRLEN_PP(cfg_directory_trigger));
		}

		efree(uri_protocol);
	}

{
    smart_str controller_path = {0};
    zend_get_constant(ZEND_STRL("APPPATH"), &app_path);
    smart_str_appendl(&controller_path, Z_STRVAL(app_path), Z_STRLEN(app_path));
    smart_str_appendl(&controller_path, "controllers", 11);
    smart_str_appendc(&controller_path, '/');
    if(Z_STRLEN_P(directory) > 0){
        smart_str_appendl(&controller_path, Z_STRVAL_P(directory), Z_STRLEN_P(directory));
        smart_str_appendc(&controller_path, '/');
    }
    smart_str_appendl(&controller_path, Z_STRVAL_P(controller), Z_STRLEN_P(controller));
    smart_str_appendl(&controller_path, ".php", 4);
    smart_str_0(&controller_path);

    php_stat(controller_path.c, controller_path.len, FS_EXISTS, &controller_exists TSRMLS_CC);

    if(Z_LVAL(controller_exists) == 1){
        char *controller_class_name;
        zend_class_entry *controller_ce;
        zval *controller_obj, construct;
        
        include(controller_path.c);
        controller_class_name = zend_str_tolower_dup(Z_STRVAL_P(controller), Z_STRLEN_P(controller));
        controller_ce = get_class_entry(controller_class_name, Z_STRLEN_P(controller));

        zend_str_tolower(Z_STRVAL_P(method), Z_STRLEN_P(method));

        if (zend_hash_exists(&controller_ce->function_table, Z_STRVAL_P(method), Z_STRLEN_P(method))) {
            efree(controller_class_name);
            goto error_404;
        }

        MAKE_STD_ZVAL(controller_obj);
        ZVAL_STRINGL(&construct, "__construct", 11, 1);
        object_init_ex(controller_obj, controller_ce);

        zend_update_property(controller_ce, controller_obj, ZEND_STRL("controller"), controller TSRMLS_CC);
        zend_update_property(controller_ce, controller_obj, ZEND_STRL("module"), method TSRMLS_CC);

		if (zend_hash_exists(&controller_ce->function_table, Z_STRVAL(construct), Z_STRLEN(construct))) {
            efree(controller_class_name);
            goto error_404;
        }else{
        	call_user_function(&controller_ce->function_table, &controller_obj, &construct, return_value, 0, NULL);
        	call_user_function(&controller_ce->function_table, &controller_obj, method, return_value, 0, NULL);
        }

        efree(controller_class_name);
        zval_ptr_dtor(&controller_obj);
        zval_dtor(method);
        zval_dtor(controller);
        zval_dtor(directory);
        RETURN_TRUE;
    }
    
}

error_404:
    if(Z_STRLEN_PP(override) > 0){
        smart_str error_path = {0};
        smart_str_appendl(&error_path, Z_STRVAL(app_path), Z_STRLEN(app_path));
        smart_str_appendc(&error_path, '/');
        smart_str_appendl(&error_path, Z_STRVAL_PP(override), Z_STRLEN_PP(override));
		smart_str_0(&error_path);
        include(error_path.c);
    }else{
        sapi_add_header("HTTP/1.0 404 Not Found", 22, 1);
    }
}

ZEND_METHOD(wk_framework, initialize){
	zval *instance;
	instance = zend_read_static_property(wk_framework_ce, ZEND_STRL("framework"), 0 TSRMLS_CC);

	if( Z_TYPE_P(instance) == IS_NULL || Z_TYPE_P(instance) != IS_OBJECT ){
		zend_error(E_ERROR, "framework object error!");
		RETURN_FALSE;
	}

	zend_call_method_with_0_params(&instance, NULL, NULL, "loadCore", NULL);
}

static zend_function_entry wk_framework_method[] = {
	ZEND_ME(wk_framework, __construct, void_arginfo, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC)
	ZEND_ME(wk_framework, initialize, void_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(wk_framework, setCompileDir, setCompileDir_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(wk_framework, setTemplateDir, setTemplateDir_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(wk_framework, loadCore, void_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(wk_framework, captureRouter, captureRouter_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(wk_framework, loadView, void_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(wk_framework, setApplicationPath, setApplicationPath_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(wk_framework, setEnvironment, setEnvironment_arginfo, ZEND_ACC_PUBLIC)
	{NULL, NULL, NULL}
};


/* {{{ PHP_MINIT_FUNCTION
 */
PHP_MINIT_FUNCTION(wk_framework)
{
	/* If you have INI entries, uncomment these lines 
	REGISTER_INI_ENTRIES();
	*/
	zend_class_entry ce;
	INIT_CLASS_ENTRY(ce, "wk_framework", wk_framework_method);
	wk_framework_ce = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);

	zend_declare_property_null(wk_framework_ce, ZEND_STRL("framework"), ZEND_ACC_STATIC | ZEND_ACC_PROTECTED TSRMLS_CC);
	zend_declare_property_null(wk_framework_ce, ZEND_STRL("view"), ZEND_ACC_STATIC | ZEND_ACC_PROTECTED TSRMLS_CC);
	zend_declare_property_null(wk_framework_ce, ZEND_STRL("config"), ZEND_ACC_STATIC | ZEND_ACC_PROTECTED TSRMLS_CC);
	zend_declare_property_null(wk_framework_ce, ZEND_STRL("apppath"), ZEND_ACC_STATIC | ZEND_ACC_PUBLIC TSRMLS_CC);
	zend_declare_property_null(wk_framework_ce, ZEND_STRL("basepath"), ZEND_ACC_STATIC | ZEND_ACC_PUBLIC TSRMLS_CC);
	zend_declare_property_stringl(wk_framework_ce, ZEND_STRL("environment"), "", 0, ZEND_ACC_STATIC | ZEND_ACC_PUBLIC TSRMLS_CC);

	EXT_STARTUP(wk_config);
	EXT_STARTUP(WK_Controller);
	EXT_STARTUP(WK_Model);
	EXT_STARTUP(wk_input);
	EXT_STARTUP(wk_load);
	EXT_STARTUP(template);

	return SUCCESS;
}
/* }}} */

/* {{{ PHP_MSHUTDOWN_FUNCTION
 */
PHP_MSHUTDOWN_FUNCTION(wk_framework)
{
	/* uncomment this line if you have INI entries
	UNREGISTER_INI_ENTRIES();
	*/
	return SUCCESS;
}
/* }}} */

/* Remove if there's nothing to do at request start */
/* {{{ PHP_RINIT_FUNCTION
 */
PHP_RINIT_FUNCTION(wk_framework)
{
	return SUCCESS;
}
/* }}} */

/* Remove if there's nothing to do at request end */
/* {{{ PHP_RSHUTDOWN_FUNCTION
 */
PHP_RSHUTDOWN_FUNCTION(wk_framework)
{
	return SUCCESS;
}
/* }}} */

/* {{{ PHP_MINFO_FUNCTION
 */
PHP_MINFO_FUNCTION(wk_framework)
{
	php_info_print_table_start();
	php_info_print_table_header(2, "wk_framework support", "enabled");
	php_info_print_table_end();

	/* Remove comments if you have entries in php.ini
	DISPLAY_INI_ENTRIES();
	*/
}
/* }}} */


/* Remove the following function when you have successfully modified config.m4
   so that your module can be compiled into PHP, it exists only for testing
   purposes. */

/* Every user-visible function in PHP should document itself in the source */
/* {{{ proto string confirm_wk_framework_compiled(string arg)
   Return a string to confirm that the module is compiled in */
PHP_FUNCTION(wk_get_instance)
{
	zval *controller;
	controller = zend_read_static_property(wk_controller_ce, ZEND_STRL("instance"), 0 TSRMLS_CC);
	RETURN_ZVAL(controller, 1, 0);
}
/* }}} */